//
// RS Game Framework
// Copyright © 2009 Jedd Haberstro
// jhaberstro@gmail.com
// 
// $Id:
//

#include <rs/Condition.hpp>
#include <rs/Mutex.hpp>
#include <rs/Thread.hpp>
#include <rs/unittest/UnitTest.hpp>
#include <vector>

namespace
{
    using namespace rs;
    using namespace rs::unittest;
    
    enum WhichThread
    {
        kThread1,
        kThread2,
        kThread3
    };
    
    enum { kWaitValue = 1 };
        
    struct TestData
    {
        int* sig;
        Condition* condition;
        Mutex* mutex;
        Boolean ran;
    };
    
    void SignalFunc(void* context) {
        TestData& data = *static_cast< TestData* >(context);
        data.mutex->Lock();
        while ((*data.sig) != kWaitValue) data.condition->Wait(*data.mutex);
        *data.sig = 0;
        data.ran = true;
        data.mutex->Unlock();
    }
            
    RS_TEST_CASE(rs::Condition, Signal)
    {
        TestData data;
        Thread thread1(SignalFunc);
        Condition condition;
        Mutex mutex;
        int sig = 0;
        
        data.sig = &sig;
        data.condition = &condition;
        data.mutex = &mutex;
        data.ran = false;
        
        thread1.Start(static_cast< void* >(&data));
        Thread::Yield();
        RS_ASSERT(data.ran == false, "thread1's 'ran' should not be true");
        
        mutex.Lock();
        sig = kWaitValue;
        condition.Signal();
        mutex.Unlock();
        
        thread1.Join();
        RS_ASSERT(data.ran == true, "thread1's 'ran' should not be false");
    }
    
    
    void BroadcastFunc(void* context) {
        TestData& data = *static_cast< TestData* >(context);
        data.mutex->Lock();
        while ((*data.sig) != kWaitValue) data.condition->Wait(*data.mutex);
        data.ran = true;
        data.mutex->Unlock();
    }
    
    RS_TEST_CASE(rs::Condition, Broadcast)
    {
        TestData data[3];
        Thread thread1(BroadcastFunc), thread2(BroadcastFunc), thread3(BroadcastFunc);
        Condition condition;
        Mutex mutex;
        int sig = 0;
        
        for (int i = 0; i < 3; ++i) {
            data[i].sig = &sig;
            data[i].condition = &condition;
            data[i].mutex = &mutex;
            data[i].ran = false;
        }
        
        thread1.Start(static_cast< void* >(&data[0]));
        thread2.Start(static_cast< void* >(&data[1]));
        thread3.Start(static_cast< void* >(&data[2]));
        Thread::Yield();
        RS_ASSERT(data[0].ran == false, "thread1's 'ran' should not be true");
        RS_ASSERT(data[1].ran == false, "thread2's 'ran' should not be true");
        RS_ASSERT(data[2].ran == false, "thread2's 'ran' should not be true");
        
        mutex.Lock();
        sig = true;
        condition.BroadcastSignal();
        mutex.Unlock();
        
        thread1.Join();
        thread2.Join();
        thread3.Join();
        RS_ASSERT(data[0].ran == true, "thread1's 'ran' should not be false");
        RS_ASSERT(data[1].ran == true, "thread2's 'ran' should be true");
        RS_ASSERT(data[2].ran == true, "thread2's 'ran' should be true");

    }
}
